package common

import (
	"cs0512/fmr/internal/apiserver/domain"
	"cs0512/fmr/internal/apiserver/store"
	"cs0512/fmr/internal/pkg/response"
	"cs0512/fmr/internal/pkg/uploader"
	"cs0512/fmr/pkg/utils"
	"cs0512/fmr/pkg/utils/sms"
	"sync"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/marmotedu/log"
)

type CommonController struct {
	srv domain.Service
}

func New(store store.Factory) *CommonController {
	return &CommonController{
		srv: domain.NewService(store),
	}
}

type Smscode struct {
	Secode string
	Period time.Time
}

type secodeMap struct {
	ls map[string][]Smscode
	mu sync.Mutex
}

var codeMap *secodeMap = &secodeMap{
	ls: make(map[string][]Smscode),
	mu: sync.Mutex{},
}

func (sm *secodeMap) Set(mobile, secode string) bool {
	ls, ok := sm.ls[mobile]
	if ok {
		sc := ls[len(ls)-1]
		if time.Now().Before(sc.Period.Add(time.Minute * -14)) {
			return false
		}
	} else {
		sm.ls[mobile] = make([]Smscode, 0)
	}

	sm.mu.Lock()
	defer sm.mu.Unlock()

	sc := Smscode{
		Secode: secode,
		Period: time.Now().Add(time.Minute * 15),
	}
	sm.ls[mobile] = append(sm.ls[mobile], sc)
	return true
}

func (sm *secodeMap) Check(mobile, secode string) bool {
	sm.mu.Lock()
	defer sm.mu.Unlock()
	ls, ok := sm.ls[mobile]
	if !ok {
		return false
	}
	for i, sc := range ls {
		if sc.Secode == secode {
			if len(ls) == 1 {
				delete(sm.ls, mobile)
			} else {
				sm.ls[mobile] = append(ls[:i], ls[i+1:]...)
			}
			return true
		}
	}
	return false
}

func (sm *secodeMap) Cleanup() {
	sm.mu.Lock()
	defer sm.mu.Unlock()

	for mobile, ss := range sm.ls {
		if len(ss) == 0 {
			delete(sm.ls, mobile)
			continue
		}

		for i := range ss {
			if time.Now().After(ss[i].Period) {
				sm.ls[mobile] = ss[:i]
				break
			}
		}

		if len(sm.ls[mobile]) == 0 {
			delete(sm.ls, mobile)
		}
	}
}

func init() {
	codeMap = &secodeMap{
		ls: make(map[string][]Smscode),
		mu: sync.Mutex{},
	}
	ticker := time.NewTicker(time.Hour * 12)
	go func(ticker *time.Ticker) {
		for range ticker.C {
			codeMap.Cleanup()
		}
	}(ticker)
}

type fileResp struct {
	Path string `json:"filepath"`
	Name string `json:"name"`
	Type string `json:"filetype"`
	Size int64  `json:"size"`
}

//文件和图片上传的验证和过滤, 在Uploader中处理了
/*
std = NewUploader(
	WithAllowTypes("jpg", "jpeg", "png", "bmp", "pdf", "xls", "xlsx", "doc", "docx", "ppt", "pptx"), //允许上传的类型
	WithMaxSize(1024*1024*10), //允许的最大值
)
*/

// 上传文件
func (co *CommonController) UploadFile(c *gin.Context) {

	file, err := c.FormFile("upfile")
	if err != nil {
		log.Errorf("upload file error:%v", err)
		response.Fail(c, 1201, "文件上传失败")
		return
	}
	//c.SaveUploadedFile(file, "./")
	uf, err := uploader.UploadFile(file)
	if err != nil {
		log.Errorf("upload file error:%v", err)
		response.Fail(c, 1201, "文件上传失败:"+err.Error())
		return
	}
	log.Infof("Upload file successed : %v", uf)
	fr := fileResp{
		Path: uf.Path + "/" + uf.Filename,
		Name: uf.Name,
		Type: uf.Type,
		Size: uf.Size,
	}
	response.OK(c, fr, "")
}

type imageRequest struct {
	Image string `json:"upfile" form:"upfile" binding:"required"`
}

// 上传图片
func (co *CommonController) UploadImage(c *gin.Context) {
	ir := imageRequest{}
	if err := c.ShouldBind(&ir); err != nil {
		log.Errorf("Upload base64 image error:%v", err)
		response.Fail(c, 1202, "图片上传失败")
		return
	}

	uf, err := uploader.UploadBase64Image(ir.Image)
	if err != nil {
		log.Errorf("Upload Base64 image error:%v", err)
		response.Fail(c, 1202, "图片上传失败")
		return
	}

	log.Infof("Upload base64 image successed : %v", uf)
	fr := fileResp{
		Path: uf.Path + "/" + uf.Filename,
		Name: uf.Name,
		Type: uf.Type,
		Size: uf.Size,
	}
	response.OK(c, fr, "")
}

type secodeRequest struct {
	Mobile string `json:"mobile" form:"mobile" binding:"required"`
}

// 发送短信验证码
func (co *CommonController) SendSecode(c *gin.Context) {
	var sr secodeRequest
	if err := c.ShouldBind(&sr); err != nil {
		log.Errorf("Send sms secode error:%v", err)
		response.Fail(c, 1301, "参数错误")
		return
	}

	code := utils.RandomDigit(6)
	if !codeMap.Set(sr.Mobile, code) {
		log.Errorf("Send sms secode failed:%s", sr.Mobile)
		response.Fail(c, 1301, "参数错误")
		return
	}

	//验证手机号，发送短信验证码
	SendSecode(sr.Mobile, code)
	response.OK(c, nil, "")
}

func CheckSecode(mobile, secode string) bool {
	return codeMap.Check(mobile, secode)
}

// 短信验证码
func SendSecode(mobile string, secode string) error {
	content := &SecodeSms{
		Secode: secode,
	}
	if mobile != "" {
		go func() {
			err := sms.GetSmsIns().Send(mobile, content)
			if err != nil {
				log.Errorf("send secode error:%+v", err)
			}
		}()
	}
	return nil
}

// 短信验证码
type SecodeSms struct {
	Secode string
}

func (s *SecodeSms) Encode() map[string]interface{} {
	return map[string]interface{}{
		"code": s.Secode,
	}
}

func (s *SecodeSms) GetTpl() string {
	return "SMS_83160124"
}
